#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

enum class Status
{
    NEW,
    CONNECTING,
    CONNECTED,
    DISCONNECTED,
    CLOSED
};

class ClientConnect
{
public:
    ClientConnect(std::string severip, uint16_t severport)
        : _sockfd(-1),
          _severip(severip),
          _severport(severport),
          _retry_intried(5),
          _retry_interval(1),
          _status(Status::NEW)
    {
    }
    void Connect()
    {
        // 申请sockfd
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "socket failed!" << std::endl;
            exit(1);
        }
        // 连接
        struct sockaddr_in sever;
        bzero(&sever, sizeof(sever));
        sever.sin_family = AF_INET;
        sever.sin_port = htons(_severport);
        sever.sin_addr.s_addr = inet_addr(_severip.c_str());

        int n = connect(_sockfd, (struct sockaddr *)&sever, sizeof(sever));
        if (n < 0)
        {
            DisConnect();
            _status = Status::DISCONNECTED;
            return;
        }
        _status = Status::CONNECTED;
    }
    void Process()
    {
        while(true)
        {
            std::string message = "hello sever!";
            ssize_t n = write(_sockfd,message.c_str(),message.size());
            if(n > 0)
            {
                char buffer[1024];
                ssize_t m = read(_sockfd,buffer,sizeof(buffer));
                if(m > 0)
                {
                    buffer[m] = 0;
                    std::cout << buffer << std::endl;
                }
                else
                {
                    _status = Status::DISCONNECTED;
                    break;
                }
            }
            else
            {
                _status = Status::CLOSED;
                break;
            }
            sleep(1)
        }
    }
    void Reconnect()
    {
        _status = Status::CONNECTING;
        int cnt = 0;
        while(true)
        {
            Connect();
            if(_status == Status::CONNECTED)
                return;
            sleep(_retry_interval);
            cnt++;
            if(cnt > _retry_intried)
            {
                _status = Status::CLOSED;
                return;
            }
        }
    }
    void DisConnect()
    {
        if(_sockfd != -1)
        {
            close(_sockfd);
            _status = Status::CLOSED;
            _sockfd = -1;
        }
    }
    ~ClientConnect()
    {
        DisConnect();
    }
    Status ConnectStatus()
    {
        return _status;
    }

private:
    int _sockfd;          // 申请的sockfd
    uint16_t _severport;  // 服务端端口号
    std::string _severip; // 服务端IP
    int _retry_intried;   // 重试次数
    int _retry_interval;  // 连接间隔
    Status _status;       // 状态
};

class TCPClient
{
public:
    TCPClient(std::string severip, uint16_t severport) : _cc(severip, severport)
    {
    }
    void Excute()
    {
        while (true)
        {
            switch (_cc.ConnectStatus())
            {
            case Status::NEW:
                _cc.Connect();
                break;
            case Status::CONNECTED:
                std::cout << "连接成功，正在通信……" << std::endl;
                _cc.Process();
                break;
            case Status::DISCONNECTED:
                std::cout << "连接失败，正在重试……" << std::endl;
                _cc.Reconnect();
                break;
            case Status::CLOSED:
                std::cout << "重连失败，正在退出……" << std::endl;
                _cc.DisConnect();
                return;
            default:
                // do nothing
                break;
            }
        }
    }
    ~TCPClient()
    {
    }
private:
    ClientConnect _cc;
};



int main(int argc,char* argv[])
{   
    if(argc!=3)
    {
        std::cout << "Usage Error!" <<std::endl;
        exit(-1);
    }
    std::string ip = argv[1];
    uint16_t port = std::stoi(argv[2]);
    TCPClient tp(ip,port);
    tp.Excute();
    return 0;
}